// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 该文件通过用类型参数替换类型参数来实现泛型类型的实例化。

package types

import (
	"errors"
	"fmt"
	"go/token"
)

// 实例化使用给定类型参数targs实例化类型orig。
// orig必须是*命名或*签名类型。如果没有错误，则
// 结果类型是同类的实例化类型（a*命名或
// a*签名）。附加到*命名类型的方法也被实例化，并且
// 与一个新的*Func关联，该*Func与原始
// 方法具有相同的位置，但函数作用域为零。
// 
// 如果ctxt为非零，则可以使用它对
// 具有相同标识的以前的实例进行重复数据消除。作为一种特殊情况，只有当泛型
// /*签名源类型是指针
// 等效类型时，它们才被认为是相同的，因此实例化不同（但可能相同）的
// 签名将产生不同的实例。
// 
// 如果设置了validate，则实例化将验证类型参数
// 的数量与参数是否匹配，以及类型参数是否满足其
// 相应的类型约束。如果验证失败，产生的错误
// 可能会包装一个*ArgumentError，指示哪个类型参数不满足
// 其相应的类型参数约束，以及为什么。
// 
// 如果未设置validate，则Instance不会验证类型参数计数
// 或类型参数是否满足其约束。Instantiate是
// 保证不会返回错误，但可能会死机。具体来说，对于
// *签名类型，如果类型参数
// /计数不正确，则Instance将立即死机；对于*命名类型，
// /*命名API中可能会出现恐慌。
func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) {
	if validate {
		var tparams []*TypeParam
		switch t := orig.(type) {
		case *Named:
			tparams = t.TypeParams().list()
		case *Signature:
			tparams = t.TypeParams().list()
		}
		if len(targs) != len(tparams) {
			return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams))
		}
		if i, err := (*Checker)(nil).verify(token.NoPos, tparams, targs); err != nil {
			return nil, &ArgumentError{i, err}
		}
	}

	inst := (*Checker)(nil).instance(token.NoPos, orig, targs, ctxt)
	return inst, nil
}

// 实例使用给定的原始类型
// 类型和参数targs创建类型或函数实例。对于命名类型，结果实例将是
// 未展开。
func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Context) (res Type) {
	var h string
	if ctxt != nil {
		h = ctxt.instanceHash(orig, targs)
		// typ可能已经用相同的类型参数进行了实例化。在
		// 这种情况下，重新使用现有实例。
		if inst := ctxt.lookup(h, orig, targs); inst != nil {
			return inst
		}
	}

	switch orig := orig.(type) {
	case *Named:
		tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
		named := check.newNamed(tname, orig, nil, nil, nil) // 解析命名时会设置底层、tparams和方法
		named.targs = newTypeList(targs)
		named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
			return expandNamed(ctxt, n, pos)
		}
		res = named

	case *Signature:
		tparams := orig.TypeParams()
		if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
			return Typ[Invalid]
		}
		if tparams.Len() == 0 {
			return orig // 无需做任何事情（轻微优化）
		}
		sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), ctxt).(*Signature)
		// 如果签名不使用其类型参数，subst 
		// 将不会复制。在这种情况下，现在就复制一份（这样我们可以将tparams设置为零，而不产生副作用）。
		if sig == orig {
			copy := *sig
			sig = &copy
		}
		// 实例化泛型签名后，它不再是泛型
		// 了；我们需要将tparams设置为零。
		sig.tparams = nil
		res = sig
	default:
		// 只有类型和函数可以是泛型的
		panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig))
	}

	if ctxt != nil {
		// 我们可能已经输掉了一场向上下文中添加命名的竞赛。
		// 在这种情况下，使用上下文中记录的任何实例。
		res = ctxt.update(h, orig, targs, res)
	}

	return res
}

// 验证塔格伦验证targs和TPARMS的长度是否匹配，
// 如果不匹配，则报告错误。如果验证失败且检查为零，
// ValidateTaglen会恐慌。
func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool {
	if ntargs != ntparams {
		// TODO（gri）提供更好的错误消息
		if check != nil {
			check.errorf(atPos(pos), _WrongTypeArgCount, "got %d arguments but %d type parameters", ntargs, ntparams)
			return false
		}
		panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, ntargs, ntparams))
	}
	return true
}

func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) {
	smap := makeSubstMap(tparams, targs)
	for i, tpar := range tparams {
		// 确保我们有一个（可能是隐式的）接口作为类型绑定（问题#51048）。
		tpar.iface()
		// 类型参数绑定使用与实例化类型相同的类型参数
		// 进行参数化；在我们可以使用它进行边界检查之前，我们需要用类型参数来实例化它，我们用类型参数实例化了参数化类型。
		bound := check.subst(pos, tpar.bound, smap, nil)
		if err := check.implements(targs[i], bound); err != nil {
			return i, err
		}
	}
	return -1, nil
}

// implements检查V是否实现T，如果没有实现，则报告错误。
// 如果通过导出的
// API调用调用implements，例如AssignableTo，则接收方可能为零。
func (check *Checker) implements(V, T Type) error {
	Vu := under(V)
	Tu := under(T)
	if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
		return nil // 避免后续错误
	}
	if p, _ := Vu.(*Pointer); p != nil && under(p.base) == Typ[Invalid] {
		return nil // 避免后续错误（参见问题#49541示例）
	}

	errorf := func(format string, args ...any) error {
		return errors.New(check.sprintf(format, args...))
	}

	Ti, _ := Tu.(*Interface)
	if Ti == nil {
		var cause string
		if isInterfacePtr(Tu) {
			cause = check.sprintf("type %s is pointer to interface, not interface", T)
		} else {
			cause = check.sprintf("%s is not an interface", T)
		}
		return errorf("%s does not implement %s (%s)", V, T, cause)
	}

	// 每种类型都满足空接口。
	if Ti.Empty() {
		return nil
	}
	// T不是空接口（即T的类型集受到限制）

	// 具有空类型集的接口V满足任何接口。
	// /（空集是任何集合的子集。）
	Vi, _ := Vu.(*Interface)
	if Vi != nil && Vi.typeSet().IsEmpty() {
		return nil
	}
	// V的类型集不为空

	// 具有非空类型集的任何类型都不满足空类型集。
	if Ti.typeSet().IsEmpty() {
		return errorf("cannot implement %s (empty type set)", T)
	}

	// V必须实现T的方法（如果有的话）。
	if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
		return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
	}

	// 如果T是可比的，那么V必须是可比的。
	// 记住这是一个未决错误，只有在没有更具体的错误时才报告。
	var pending error
	if Ti.IsComparable() && !comparable(V, false, nil, nil) {
		pending = errorf("%s does not implement comparable", V)
	}

	// V也必须在T的类型集中，如果有的话。
	// 上面已经排除了带有空类型集的约束。
	if !Ti.typeSet().hasTerms() {
		return pending // 无需操作
	}

	// 如果V本身是一个接口，其每个可能的类型都必须在T类型的集合
	// 中（即，V类型集合必须是T类型集合的子集）。
	// 上面已经排除了带有空类型集的接口V。
	if Vi != nil {
		if !Vi.typeSet().subsetOf(Ti.typeSet()) {
			// TODO（gri）报告缺少哪种类型
			return errorf("%s does not implement %s", V, T)
		}
		return pending
	}

	// 否则，V的类型必须包含在iface类型集中。
	var alt Type
	if Ti.typeSet().is(func(t *term) bool {
		if !t.includes(V) {
			// 如果V∉ t、 典型但V∈ ~t、 typ然后记住这个类型
			// 因此我们可以在错误
			// 消息中建议它作为替代。
			if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) {
				tt := *t
				tt.tilde = true
				if tt.includes(V) {
					alt = t.typ
				}
			}
			return true
		}
		return false
	}) {
		if alt != nil {
			return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T)
		} else {
			return errorf("%s does not implement %s", V, T)
		}
	}

	return pending
}
